package com.atxiaodei.nio.nioday01;

import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author wangmeng
 * @date 2020/9/12
 *
 * Stream管道流的map操作
 **/
public class StreamApi4 {

    List<String> list = Arrays.asList("Monkey", "Lion", "Giraffe", "Lemur");


    List<StreamTestVo> listStream = Arrays.asList(
            new StreamTestVo(1, 23, "M", "Rick", "Beethovan"),
            new StreamTestVo(2, 13, "F", "Martina", "Hengis"),
            new StreamTestVo(3, 43, "M", "Ricky", "Martin"),
            new StreamTestVo(4, 26, "M", "Jon", "Lowman"),
            new StreamTestVo(5, 19, "F", "Cristine", "Maria"),
            new StreamTestVo(6, 15, "M", "David", "Feezor"),
            new StreamTestVo(7, 68, "F", "Melissa", "Roy"),
            new StreamTestVo(8, 79, "M", "Alex", "Gussin"),
            new StreamTestVo(9, 15, "F", "Neetu", "Singh"),
            new StreamTestVo(10, 45, "M", "Naveen", "Jain")
    );
    // 将集合中的每一个字符串，全部转换成大写！
    @Test
    public  void test1(){
        // ① 先用匿名内部类 了解底层
        list.stream()
                .map(new Function<String, Object>() {
                    @Override
                    public Object apply(String s) {
                        return s.toUpperCase();
                    }
                })
                .collect(Collectors.toList())
                .forEach(System.out::println);
        System.out.println("-----------------------------------------------");
        // ② 用lamdba完成
        list.stream()
                .map(x->x.toUpperCase())
                .collect(Collectors.toList())
                .forEach(System.out::println);
        System.out.println("-----------------------------------------------");
        // ③ 用方法引用完成
        list.stream()
                .map(String::toUpperCase)
                .collect(Collectors.toList())
                .forEach(System.out::println);
    }

    /**
     * 处理非字符串类型集合元素
     */
    @Test
    public void test2(){
        // ① map()函数不仅可以处理数据，还可以转换数据的类型
        list.stream()
                .map(String::length)
                .collect(Collectors.toList())
                .forEach(System.out::println);
        System.out.println("--------------------------------------------------------");
        // ② 除了mapToInt。还有maoToLong，mapToDouble等等用法
        IntStream intStream = list.stream()
                .mapToInt(String::length);
        intStream.forEach(System.out::println);
    }

    /**
     * 处理对象数据格式转换
     * 将每一个StreamTestVo的年龄增加一岁
     * 将性别中的“M”换成“male”，F换成Female。
     */
    @Test
    public void test3(){
        // Stream + lamdba实现
        listStream.stream()
                    .map(x -> {
                        x.setAge(x.getAge()+1);
                        x.setGender(x.getGender().equals("M")?"male":"female");
                        return x;
                    })
                    .collect(Collectors.toList())
                    .forEach(System.out::println);
        System.out.println("-----------------------------------------------------------");
        // Stream + 匿名内部类实现
         listStream.stream()
                .map(new Function<StreamTestVo, Object>() {
                    @Override
                    public Object apply(StreamTestVo streamTestVo) {
                        streamTestVo.setAge(streamTestVo.getAge()+1);
                        streamTestVo.setGender(streamTestVo.getGender().equals("M")?"male":"female");
                        return streamTestVo;
                    }
                }).collect(Collectors.toList());
        System.out.println("-----------------------------------------------------------");
         // 由于map的参数e就是返回值，所以可以用peek函数。peek函数是一种特殊的map函数，
        // 当函数没有返回值或者参数就是返回值的时候可以使用peek函数。
        // Stream + lamdba实现
        listStream.stream()
                .peek(x -> {
                    x.setAge(x.getAge()+1);
                    x.setGender(x.getGender().equals("M")?"male":"female");
                })
                .collect(Collectors.toList())
                .forEach(System.out::println);
        System.out.println("-----------------------------------------------------------");
        listStream.stream()
                .peek(x -> x.setAge(x.getAge()+1)) // 当 Lambda 体中只有一条语句时，可以这么写，而map就不可以(为什么呢，因为set方法压根就没有返回值)
                .collect(Collectors.toList())
                .forEach(System.out::println);
    }

    /**
     * map可以对管道流中的数据进行转换操作，但是如果管道中还有管道该如何处理？即：如何处理二维数组及二维集合类。
     * 实现一个简单的需求：将“hello”，“world”两个字符串组成的集合，元素的每一个字母打印出来。如果不用Stream我们怎么写？
     * 写2层for循环,第一层遍历字符串，并且将字符串拆分成char数组，第二层for循环遍历char数组
     * 用map方法是做不到的，这个需求用map方法无法实现。map只能针对一维数组进行操作，数组里面还有数组，管道里面还有管道，它是处理不了每一个元素的
     *
     * 解决方案：flatMap可以理解为将若干个子管道中的数据全都，平面展开到父管道中进行处理。
     */
    @Test
    public void test4(){
        List<String> list = Arrays.asList("hello", "word");
        // 用map方法是做不到的，这个需求用map方法无法实现。map只能针对一维数组进行操作，数组里面还有数组，管道里面还有管道，它是处理不了每一个元素的
        list.stream()
                .map(x->x.split(""))
                .collect(Collectors.toList())
                .forEach(System.out::println);// {{h,e,l,l,o},{w,o,r,d}.....}

        // 虽然用map最终还是解决了两侧管道的，但是呢，如果多层管道呢，是不是里面要嵌套好多层Stream的，预期这样Stream有这个这样子的方法很方便flatMap
         list.stream()
                .map(x -> x.split(""))
                .forEach((sm) -> {
                    Arrays.stream(sm)
                            .collect(Collectors.toList())
                            .forEach(System.out::println);
        });
         // 解决方案：flatMap可以理解为将若干个子管道中的数据全都，平面展开到父管道中进行处理。
        System.out.println("--------------------------------------------------------------------------------");
        list.stream()
                .flatMap(x->Arrays.stream(x.split("")))
                .collect(Collectors.toList())
                .forEach(System.out::println);
    }


}
